<?php

/**
 * redis 缓存
 */
class Cache_Redis extends Cache_Abstract {

    /**
     * 实例化的 Redis 对像
     * @var Redis
     */
    protected $_redisInstances = array();

    // 数据类型前缀
    const DATA_PREFIX_STRING = 's';
    const DATA_PREFIX_OBJECT = 'o';

    /**
     * 构造函数
     *
     * @param  array  $option
     * @throws Exception
     */
    public function __construct($option = array()) {

        if (!class_exists('Redis')) {
            throw new Cache_Exception('Redis extension is not ON', Cache_Exception::E_CACHE_NOT_SUPPORT);
        }

        // 参数
        $option['life_time'] = !empty($option['life_time']) ? intval($option['life_time']) : 864000000;
        if (empty($option['servers']) || !is_array($option['servers'])) {
            $option['servers'] = array(
                array(
                    'host' => '127.0.0.1',
                    'port' => 6379,
                    'timeout' => 3,
                )
            );
        }

        parent::__construct($option);
    }

    /**
     * 根据 key 获取一个连接
     * @param string $key
     */
    private function _getRedis($key) {

        // hash
        $hash = crc32($key) % count($this->_option['servers']);
        return $this->_getRedisByHash($hash);
    }

    /**
     * 获取指定 index 的redis连接实例
     * @param type $hash
     * @return type
     * @throws Cache_Exception
     */
    private function _getRedisByHash($hash) {

        if (empty($this->_redisInstances[$hash])) {
            try {
                // [TODO] 改用 RedisConn 获取 redis 实例，以便全局复用连接
                $this->_redisInstances[$hash] = new Redis();
                $this->_redisInstances[$hash]->connect($this->_option['servers'][$hash]['host'],
                    $this->_option['servers'][$hash]['port'],
                    $this->_option['servers'][$hash]['timeout']);
            } catch (Exception $e) {
                throw new Cache_Exception($e);
            }
        }

        return $this->_redisInstances[$hash];
    }

    /**
     * 保存缓存资料
     *
     * @param  mixed   $data
     * @param  string  $id
     * @return boolean
     */
    public function save($id, $data, $expired = null) {

        if (null === $expired) {
            $expired = $this->_option['life_time'];
        }
        return $this->_getRedis($id)->setex($id, $expired, $this->_encodeData($data));
    }

    /**
     * 加载缓存数据
     *
     * @param  string   $id
     * @return mixed|false
     */
    public function load($id) {

        return $this->_decodeData($this->_getRedis($id)->get($id));
    }

    /**
     * 移除缓存数据
     *
     * @param  string  $id
     * @return boolean
     */
    public function remove($id) {

        return $this->_getRedis($id)->del($id);
    }

    /**
     * 移除所有缓存数据
     *
     * @return boolean
     */
    public function clean() {

        foreach (array_keys($this->_option['servers']) as $idx) {
            $this->_getRedisByHash($idx)->flushAll();
        }
    }

    /**
     * 将任意数据编码为字符串
     * @param <type> $data
     * @return string
     */
    private function _encodeData($data) {

        if (is_string($data)) {
            return self::DATA_PREFIX_STRING.':'.$data;
        } else {
            return self::DATA_PREFIX_OBJECT.':'.json_encode($data);
        }
    }

    /**
     * 将编码字符串解码
     * @param <type> $data
     * @return <type>
     */
    private function _decodeData($data) {

        if (!$data) {
            return false;
        }
        list($prefix, $content) = explode(':', $data, 2);
        if ($prefix == self::DATA_PREFIX_OBJECT) {
            return json_decode($content, true);
        }
        return $content;
    }
}
